﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Threading;
using System.Windows;
using System.Windows.Resources;

namespace Galaktika.BI.Silverlight.ClientApp
{
    using Galaktika.BI.Extensibility;
    using Galaktika.BI.Silverlight.ClientApp.Services;
    using Galaktika.BI.Runtime.Services;
    using Galaktika.BI.Silverlight.Services;
    using Galaktika.BI.Extensibility.Dom.Dac;
    using Galaktika.BI.Extensibility.Dom;
    using Galaktika.BI.Silverlight.Services.MetaService;
    using System.Globalization;

    public class Downloader
    {
        private class DownloadTask
        {
            public DownloadTask(string name)
            {
                this.Name = name;
            }

            public readonly string Name;
            public WaitCallback Callback;
            public WaitCallback OnSuccess;
            public WaitCallback OnError;
            public UserTaskDescriptor WorkItem;
            public Func<string> GetErrorMessage;
        }

        static WebClient m_Client;
        private static WebClient Client
        {
            get
            {
                if (m_Client == null)
                {
                    m_Client = new WebClient();
                    m_Client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
                    m_Client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
                    m_Client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted);
                }

                return m_Client;
            }
        }

        static void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            ProcessDownloadComplete(e);
        }

        static void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
        }

        static void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            ProcessDownloadComplete(e);
        }

        static void ProcessDownloadComplete(AsyncCompletedEventArgs e)
        {
            IUserTaskManager taskManager = App.CurrentApp.GetService<IUserTaskManager>();
            if (taskManager != null)
            {
                Queue<DownloadTask> queue = e.UserState as Queue<DownloadTask>;
                if (queue != null)
                {
                    DownloadTask task = queue.Dequeue();
                    if (e.Error == null)
                    {
                        taskManager.DoneTask(task.WorkItem);
                        if (task.OnSuccess != null)
                        {
                            task.OnSuccess.Invoke(e);
                        }

                        ProcessDownloadQueue(queue);
                    }
                    else
                    {
                        taskManager.AbortTask(task.WorkItem, e.Error);
                        if (task.OnError != null)
                        {
                            task.OnError.Invoke(e);
                        }

                        ProcessDownloadQueue(queue);
                    }
                }
            }
        }

        public static void DownloadAll()
        {
            Queue<DownloadTask> queue = new Queue<DownloadTask>();

            /*
            var taskMgr = App.CurrentApp.GetService<IUserTaskManager>();
            if (taskMgr != null)
            {
                taskMgr.RunTaskAsync(Localization.AppTask_LoadingSettings, _ =>
                {
                    using (var srv = new SyncMetaService())
                    {
                        var settings = srv.GetUserSettings();
                        if (settings != null)
                        {
                            App.CurrentApp.Binder.InitializeSettings(settings);
                        }
                    }
                });
            }
            */

            var task = new DownloadTask(Localization.AppTask_DownloadingSetting);
            task.Callback = _ => Downloader.Client.DownloadStringAsync(new Uri(Galaktika.BI.Silverlight.Consts.SettingsContainerName, UriKind.Relative), queue);
            task.OnSuccess = DownloadSettingsComplete;
            queue.Enqueue(task);

            task = new DownloadTask(Localization.AppTask_DowloadingClientPack);
            task.Callback = state => Downloader.Client.OpenReadAsync(new Uri(Galaktika.BI.Silverlight.Consts.ClientPackName, UriKind.Relative), queue);
            task.OnSuccess = DownloadClientPackComplete;
            task.OnError = DownloadClientPackError;
            task.GetErrorMessage = () => 
                string.Format(Localization.Exc_ErrorDownloadingItem,
                    new Uri(
                        new Uri(DataBinder.Current.UserSettings.Settings.BaseUri, UriKind.Absolute),
                        new Uri(Galaktika.BI.Silverlight.Consts.ClientPackName, UriKind.Relative)).ToString());
            queue.Enqueue(task);

            ProcessDownloadQueue(queue);
        }

        static void srv_GetUserSettingsAsyncCompleted(object sender, GetUserSettingsCompletedEventArgs e)
        {
            ProcessDownloadComplete(e);
        }

        private static void DownloadClientPackError(object state)
        {
            OpenReadCompletedEventArgs e = state as OpenReadCompletedEventArgs;
            if (e != null)
            {
                Queue<DownloadTask> queue = new Queue<DownloadTask>();

				DownloadTask task = new DownloadTask(Localization.AppTask_DownloadingClientDll);
                task.Callback = userState => Downloader.Client.OpenReadAsync(new Uri(Galaktika.BI.Silverlight.Consts.ClientDllName, UriKind.Relative), queue);
                task.OnSuccess = DownloadClientDllComplete;
                queue.Enqueue(task);

                task = new DownloadTask(Localization.AppTask_DownloadingDac);
                task.Callback = userState => Downloader.Client.DownloadStringAsync(new Uri(Galaktika.BI.Silverlight.Consts.DALContainerName, UriKind.Relative), queue);
                task.OnSuccess = DownloadDacComplete;
                queue.Enqueue(task);

                task = new DownloadTask(Localization.AppTask_DownloadingMetadata);
                task.Callback = userState => Downloader.Client.DownloadStringAsync(new Uri(Galaktika.BI.Silverlight.Consts.MetadataContainerName, UriKind.Relative), queue);
				task.OnSuccess = DownloadMetadataComplete;
				queue.Enqueue(task);

                //task = new DownloadTask(Localization.AppTask_DownloadingUserSettings);
                //task.Callback = userState =>
                //{
                //};
                //task.OnSuccess = DownloadUserSettingsComplete;
                //queue.Enqueue(task);

                ProcessDownloadQueue(queue);
            }
        }

        private static void DownloadClientDllComplete(object state)
        {
            OpenReadCompletedEventArgs e = state as OpenReadCompletedEventArgs;
            if (e != null)
            {
                try
                {
                    AssemblyPart asm = new AssemblyPart();
                    ClientResources.Initialize(asm.Load(e.Result));
                }
                catch (Exception exc)
                {
                    App.CurrentApp.LogError(exc);
                }
            }
        }

        private static void DownloadMetadataComplete(object state)
        {
            DownloadStringCompletedEventArgs e = state as DownloadStringCompletedEventArgs;
            if (e != null)
            {
                try
                {
                    MetaContainer container = MetaContainer.FromXml(e.Result);
                    App.CurrentApp.Initialize(container);
                    var tm = App.CurrentApp.GetService<IUserTaskManager>();
                    if (tm != null)
                    {
                        tm.RunTaskAsync(Localization.AppTask_DownloadingUserSettings, _ =>
                        {
                            using (var srv = new SyncMetaService())
                            {
                                //srv.GetUserSettingsAsyncCompleted += new EventHandler<Galaktika.BI.Silverlight.Services.MetaService.GetUserSettingsCompletedEventArgs>(srv_GetUserSettingsAsyncCompleted);
                                //srv.GetUserSettingsAsync(queue);
                                var s = srv.GetUserSettings();
                                tm.Dispatcher.BeginInvoke(() => ProcessUserSettings(s));
                            }
                        });
                    }
                }
                catch (Exception exc)
                {
                    App.CurrentApp.LogError(exc);
                }
            }
        }

        private static void DownloadClientPackComplete(object state)
        {
            OpenReadCompletedEventArgs e = state as OpenReadCompletedEventArgs;
            if (e != null)
            {
                string mimeType = null;
                // Client.dll
                //
				StreamResourceInfo sri = App.GetResourceStream(
                    new StreamResourceInfo(e.Result, mimeType),
                    new Uri(Galaktika.BI.Silverlight.Consts.ClientDllName, UriKind.Relative));
                if (sri != null)
                {
                    try
                    {
                        AssemblyPart asm = new AssemblyPart();
                        ClientResources.Initialize(asm.Load(sri.Stream));
                    }
                    catch (Exception exc)
                    {
                        App.CurrentApp.LogError(exc);
                    }
                }

                // dal.xml
                //
                sri = App.GetResourceStream(
                    new StreamResourceInfo(e.Result, mimeType),
                    new Uri(Galaktika.BI.Silverlight.Consts.DALContainerName, UriKind.Relative));
                if (sri != null)
                {
                    try
                    {
                        BrickDacContainer container = BrickDacContainer.FromStream(sri.Stream);
                        App.CurrentApp.Binder.InitializeDac(container);
                    }
                    catch (Exception exc)
                    {
                        App.CurrentApp.LogError(exc);
                    }
                }

				// metadata.xml
				//
				sri = App.GetResourceStream(
					new StreamResourceInfo(e.Result, mimeType),
                    new Uri(Galaktika.BI.Silverlight.Consts.MetadataContainerName, UriKind.Relative));
				if (sri != null)
				{
					try
					{
						MetaContainer container = MetaContainer.FromStream(sri.Stream);
						App.CurrentApp.Initialize(container);
					}
					catch (Exception exc)
					{
						App.CurrentApp.LogError(exc);
					}
				}
            }

            //Queue<DownloadTask> queue = new Queue<DownloadTask>();
            //var task = new DownloadTask(Localization.AppTask_DownloadingUserSettings);
            //task.Callback = userState =>
            //{
            //    using (var srv = new SyncMetaService())
            //    {
            //        srv.GetUserSettingsAsyncCompleted += new EventHandler<Galaktika.BI.Silverlight.Services.MetaService.GetUserSettingsCompletedEventArgs>(srv_GetUserSettingsAsyncCompleted);
            //        srv.GetUserSettingsAsync(queue);
            //    }
            //};
            //task.OnSuccess = DownloadUserSettingsComplete;
            //queue.Enqueue(task);

            //ProcessDownloadQueue(queue);

            var tm = App.CurrentApp.GetService<IUserTaskManager>();
            if (tm != null)
            {
                tm.RunTaskAsync(Localization.AppTask_DownloadingUserSettings, _ =>
                {
                    using (var srv = new SyncMetaService())
                    {
                        //srv.GetUserSettingsAsyncCompleted += new EventHandler<Galaktika.BI.Silverlight.Services.MetaService.GetUserSettingsCompletedEventArgs>(srv_GetUserSettingsAsyncCompleted);
                        //srv.GetUserSettingsAsync(queue);
                        var s = srv.GetUserSettings();
                        tm.Dispatcher.BeginInvoke(() => ProcessUserSettings(s));
                    }
                });
            }
        }

        private static void DownloadDacComplete(object state)
        {
            DownloadStringCompletedEventArgs e = state as DownloadStringCompletedEventArgs;
            if (e != null)
            {
                try
                {
                    BrickDacContainer setting = BrickObject.FromXml<BrickDacContainer>(e.Result);
                    App.CurrentApp.Binder.InitializeDac(setting);
                }
                catch (Exception exc)
                {
                    App.CurrentApp.LogError(exc);
                }
            }
        }

        private static void ProcessUserSettings(UserSettings userSettings)
        {
            if (userSettings == null) return;
            try
            {
                Settings setting = userSettings.Settings;
                ServiceManager.Initialize(setting);
                App.CurrentApp.Binder.InitializeSettings(userSettings);

                if (!string.IsNullOrEmpty(setting.CultureName))
                {
                    Thread.CurrentThread.CurrentUICulture = new CultureInfo(setting.CultureName);
                    ((Page)App.CurrentApp.Root).UpdateLocalizableResources();
                }

                if (setting.EnableLogging)
                {
                    OutputManager.Initialize(((Page)App.CurrentApp.Root.RootControl).Output.TextBoxOutput);
                    ((Page)App.CurrentApp.Root.RootControl).ButtonViewOutput.Visibility = Visibility.Visible;
                }
                else
                {
                    OutputManager.Initialize(null);
                    ((Page)App.CurrentApp.Root.RootControl).ButtonViewOutput.Visibility = Visibility.Collapsed;
                }
                //if (setting.EnableDebugging)
                //{
                //    ((Page)App.CurrentApp.Root.RootControl).btnDebug.Visibility = Visibility.Visible;
                //}
                if (setting.EnableClientCaching)
                {
                    SyncDataService.EnableCache(App.CurrentApp.Binder.GetStatusHash);
                }
                else
                {
                    SyncDataService.DisableCache();
                }
                App.CurrentApp.Binder.AccessMode = setting.DataAccessMode;
                UserTaskThreadPool.MaxThreads = setting.MaxUserTaskThreads;
            }
            catch (Exception exc)
            {
                App.CurrentApp.LogError(exc);
            }
        }

        private static void DownloadSettingsComplete(object state)
        {
            DownloadStringCompletedEventArgs e = state as DownloadStringCompletedEventArgs;
            if (e != null)
            {
                try
                {
                    Settings setting = XmlUtility.XmlStr2Obj<Settings>(e.Result);
                    ServiceManager.Initialize(setting);
                    App.CurrentApp.Binder.UserSettings.Settings = setting;

                    if (setting.EnableLogging)
                    {
                        OutputManager.Initialize(((Page)App.CurrentApp.Root.RootControl).Output.TextBoxOutput);
                        ((Page)App.CurrentApp.Root.RootControl).ButtonViewOutput.Visibility = Visibility.Visible;
                    }
                    //if (setting.EnableDebugging)
                    //{
                    //    ((Page)App.CurrentApp.Root.RootControl).btnDebug.Visibility = Visibility.Visible;
                    //}
                    if (setting.EnableClientCaching)
                    {
                        SyncDataService.EnableCache(App.CurrentApp.Binder.GetStatusHash);
                    }
                    App.CurrentApp.Binder.AccessMode = setting.DataAccessMode;
                    UserTaskThreadPool.MaxThreads = setting.MaxUserTaskThreads;

                }
                catch (Exception exc)
                {
                    App.CurrentApp.LogError(exc);
                }
            }
        }

        private static void ProcessDownloadQueue(Queue<DownloadTask> queue)
        {
            IUserTaskManager taskManager = App.CurrentApp.GetService<IUserTaskManager>();
            if (taskManager != null)
            {
                if (queue.Count > 0)
                {
                    DownloadTask task = queue.Peek();
                    if (task != null && task.Callback != null)
                    {
                        UserTaskDescriptor wi = taskManager.CreateTask(task.Name);
                        wi.GetErrorMessage = task.GetErrorMessage;
                        task.WorkItem = wi;
                        task.Callback.Invoke(null);
                    }
                }
            }
        }
    }
}